package com.zhiwei.codesecurity.common.webservice.checkmarx.wsdk.client;

import com.zhiwei.codesecurity.common.webservice.checkmarx.wsdk.args.BaseArgs;
import com.zhiwei.codesecurity.common.utils.BeanUtil;
import com.zhiwei.codesecurity.common.utils.StringUtils;
import com.zhiwei.codesecurity.common.webservice.checkmarx.portal.v7.*;
import com.zhiwei.codesecurity.common.webservice.checkmarx.wsdk.common.ReportStatus;
import com.zhiwei.codesecurity.common.webservice.checkmarx.wsdk.common.ReportType;
import com.zhiwei.codesecurity.common.webservice.checkmarx.wsdk.common.ScanData;
import com.zhiwei.codesecurity.common.webservice.checkmarx.wsdk.common.TaskResult;
import com.zhiwei.codesecurity.common.enums.TaskStatus;
import com.zhiwei.codesecurity.common.webservice.checkmarx.wsdk.exceptions.WSException;
import com.zhiwei.codesecurity.common.webservice.checkmarx.wsdk.utils.PortalUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.xml.ws.BindingProvider;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

public abstract class PortalClient<Args extends BaseArgs> {
    private static final Logger logger = LoggerFactory.getLogger(PortalClient.class);

    public static final int LCID = 2052;// ZH_CN
//    public static final String ORIGIN = "SDKClient";
//    public static final int MAX_REPORT_RETRY_TIMES = 20;
//    public static final int REPORT_RETRY_TIMEOUT = 3000;

    private CxPortalWebServiceSoap portal;
    private Group[] groups = {};
    private ConfigurationSet[] configs = {};
    private Preset[] presets = {};
    private String sessionId;
    private CliScanArgs scanArgs;
    protected Args args;

    public PortalClient(Args args) {
        this.args = args;
        if (this.login()) {
            this.getScanEnvConfig();
        }
    }

    /**
     * 检查扫描参数是否符合要求
     * @return
     */
    private boolean check() {
        if (null == args) {
            logger.error(">>>>>>>>>>args参数为空!|args:{}", args);
            return false;
        }
        if (StringUtils.isBlank(args.getSdkUrl()) || StringUtils.isBlank(args.getSdkUsr()) ||
                StringUtils.isBlank(args.getSdkPwd())) {
            logger.error(">>>>>>>>>>args参数必填字段为空!|args:{}", args);
            return false;
        }
        return true;
    }

    /**
     * 登录WebService，获取SessionId
     * @return
     * @throws WSException
     */
    public boolean login() throws WSException {
        if (!check()) {
            throw new WSException("Args参数检验失败!");
        }

        try {
            if (null == portal) {
                this.portal = new CxPortalWebService(new URL(args.getSdkUrl())).getCxPortalWebServiceSoap();
                BindingProvider bindingProvider = (BindingProvider) portal;
                bindingProvider.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY,
                        args.getSdkUrl());
            }
        } catch (MalformedURLException ex) {
            throw new WSException(ex.getMessage());
        }

        Credentials credentials = new Credentials();
        credentials.setUser(this.args.getSdkUsr());
        credentials.setPass(this.args.getSdkPwd());
        CxWSResponseLoginData responseLoginData = portal.login(credentials, LCID);
        if (!responseLoginData.isIsSuccesfull()) {
            logger.error(">>>>>>>>>>当用用户登录WebService失败!|登录失败错误信息:{}", responseLoginData.getErrorMessage());
            throw new WSException(responseLoginData.getErrorMessage());
        }

        sessionId = responseLoginData.getSessionId();
        logger.info(">>>>>>>>>>>当用用户登录WebService成功!|sessionId:{}", sessionId);
        return true;
    }

    /**
     * 登出
     */
    public void logout() {
        logger.info(">>>>>>>>>登出WebService开始|sessionId:{}", this.sessionId);
        CxWSBasicRepsonse basicRepsonse = this.portal.logout(this.sessionId);
        if (!basicRepsonse.isIsSuccesfull()) {
            logger.error(">>>>>>>>>登出WebService失败!|错误信息:{}", basicRepsonse.getErrorMessage());
            throw new WSException(basicRepsonse.getErrorMessage());
        }
        logger.info(">>>>>>>>>登出WebService成功|sessionId:{}", this.sessionId);
    }

    /**
     * 获取扫描环境配置：AssociatedGroupsList、ConfigurationSetList、PresetList
     * @throws WSException
     */
    private void getScanEnvConfig() throws WSException {
        // 获取AssociatedGroupsList
        CxWSResponseGroupList groupList = portal.getAssociatedGroupsList(sessionId);
        if (groupList.isIsSuccesfull()) {
            groups = groupList.getGroupList().getGroup().toArray(groups);
            logger.info(">>>>>>>>>获取获取AssociatedGroupsList成功!|groups:{}", groups);
        } else {
            logger.error(">>>>>>>>>获取AssociatedGroupsList失败!|错误信息:{}", groupList.getErrorMessage());
            throw new WSException(groupList.getErrorMessage());
        }

        // 获取ConfigurationSetList
        CxWSResponseConfigSetList configSetList = portal.getConfigurationSetList(sessionId);
        if (configSetList.isIsSuccesfull()) {
            configs = configSetList.getConfigSetList().getConfigurationSet().toArray(configs);
            logger.info(">>>>>>>>>获取ConfigurationSetList成功!|configs:{}", configs);
        } else {
            logger.error(">>>>>>>>>获取ConfigurationSetList失败!|错误信息:{}", configSetList.getErrorMessage());
            throw new WSException(configSetList.getErrorMessage());
        }

        // 获取PresetList
        CxWSResponsePresetList presetList = portal.getPresetList(sessionId);
        if (presetList.isIsSuccesfull()) {
            presets = presetList.getPresetList().getPreset().toArray(presets);
            logger.info(">>>>>>>>>获取PresetList成功!|presets:{}", presets);
        } else {
            logger.error(">>>>>>>>>获取PresetList失败!|错误信息:{}", presetList.getErrorMessage());
            throw new WSException(presetList.getErrorMessage());
        }
    }

    /**
     * 创建WebService扫描参数
     */
    private void createScanArgs() {
        scanArgs = new CliScanArgs();
        scanArgs.setPrjSettings(this.createProjectSettings());
        scanArgs.setSrcCodeSettings(this.createSourceCodeSettings());
        scanArgs.setIsPrivateScan(false);
        scanArgs.setIsIncremental(false);
        scanArgs.setIgnoreScanWithUnchangedCode(false);
    }

    /**
     * Create ProjectSettings; 配置项目属性
     * @return
     */
    private ProjectSettings createProjectSettings() {
        ProjectSettings settings = new ProjectSettings();
        settings.setProjectName(args.getProjectName());
        settings.setPresetID(PortalUtil.getPresetIDByName(presets, args.getPreset()));
        settings.setAssociatedGroupID(PortalUtil.getAssociatedGroupIDByName(groups, args.getGroup()));
        settings.setScanConfigurationID(PortalUtil.getScanConfigurationIDByName(configs, args.getConfig()));
        settings.setOwner(args.getOwner());
        settings.setDescription(args.getDescription());
        return settings;
    }

    /**
     * Create SourceCodeSettings; 配置源码获取相关属性,具体逻辑由子类实现
     * @return
     */
    protected abstract SourceCodeSettings createSourceCodeSettings();

    /**
     * 提交扫描任务，返回任务结果（异步）
     * @return
     * @throws WSException
     */
    public TaskResult submitScanTask() throws WSException {
        logger.info(">>>>>>>>>>>开始提交扫描任务|sessionId:{}", this.sessionId);
        TaskResult taskResult = new TaskResult();
        taskResult.setSuccesfull(false);
        this.createScanArgs();

//        CxWSResponseRunID responseRunID = this.sdk.scanWithOriginName(this.sessionId, this.scanArgs, ORIGIN);
        CxWSResponseRunID responseRunID = this.portal.scan(this.sessionId, this.scanArgs);
        if (!responseRunID.isIsSuccesfull()) {
            logger.error(">>>>>>>>>获取responseRunID失败!|错误信息:{}", responseRunID.getErrorMessage());
            throw new WSException(responseRunID.getErrorMessage());
        }
        logger.info(">>>>>>>>>扫描参数|scanArgs:{}", this.scanArgs);
        String runId = responseRunID.getRunId();
        taskResult.setProjectId(this.scanArgs.getPrjSettings().getProjectID());
        taskResult.setSubmitRunId(Long.parseLong(runId));
        taskResult.setSuccesfull(true);

        logger.info(">>>>>>>>>提价扫描任务完成|TaskResult:{}", taskResult);
        return taskResult;
    }

    /**
     * 查询扫描任务结果
     * @param runId
     * @return
     * @throws WSException
     */
    public static TaskResult getScanStatus(PortalClient portalClient, String runId)  {
        CxWSResponseScanStatus scanStatus = portalClient.portal.getStatusOfSingleScan(portalClient.sessionId, runId);
        if (!scanStatus.isIsSuccesfull()) {
            logger.error(">>>>>>>>>查询扫描任务结果失败!|runId:{}, 错误信息:{}", runId, scanStatus.getErrorMessage());
            //throw new WSException(scanStatus.getErrorMessage());
            return null;
        }
        TaskResult taskResult = PortalUtil.parseCxWSResponseScanStatus(scanStatus);
        logger.info(">>>>>>>>>查询扫描任务结果|TaskResult:{}", taskResult);
        return taskResult;
    }

    /**
     * 创建扫描结果报告
     * @param scanId
     * @param reportType
     * @return
     * @throws WSException
     */
    public static long  createScanReport(PortalClient portalClient, long scanId, ReportType reportType)
            throws WSException {

        CxWSFilteredReportRequest reportRequest = new CxWSFilteredReportRequest();
        reportRequest.setType(PortalUtil.parseReportType(reportType));
        reportRequest.setScanID(scanId);

        CxWSCreateReportResponse createReportResponse = portalClient.portal.createScanReport(portalClient.sessionId, reportRequest);
        if (!createReportResponse.isIsSuccesfull()) {
            logger.error(">>>>>>>>>创建扫描报告失败!|scanId:{}, 错误信息:{}", scanId, createReportResponse.getErrorMessage());
            return -1L;
        }
        long reportId = createReportResponse.getID();
        return reportId;
    }

    /**
     * 获取扫描结果报告状态
     * @param portalClient
     * @param reportId
     * @return
     * @throws WSException
     */
    public static ReportStatus getReportStatus(PortalClient portalClient, long reportId) throws WSException {
        CxWSReportStatusResponse reportStatusResponse  = portalClient.portal.getScanReportStatus(portalClient.sessionId, reportId);
        if (!reportStatusResponse.isIsSuccesfull()) {
            logger.error(">>>>>>>>>查询扫描报告状态失败!|reportId:{}, 错误信息:{}", reportId, reportStatusResponse.getErrorMessage());
            return ReportStatus.FAILED;
        }
        if (reportStatusResponse.isIsFailed()) {
            return ReportStatus.FAILED;
        } else if (reportStatusResponse.isIsReady()) {
            return ReportStatus.COMPLETED;
        } else {
            return ReportStatus.PROCESSING;
        }
    }

    /**
     * 获取扫描结果报告
     * @param reportId
     * @return
     */
    public static byte[] getReport(PortalClient portalClient, long reportId)  {

        CxWSResponseScanResults scanResults = portalClient.portal.getScanReport(portalClient.sessionId, reportId);
        if (!scanResults.isIsSuccesfull()) {
            logger.error(">>>>>>>>>获取扫描报告失败!|reportId:{}, 错误信息:{}", reportId, scanResults.getErrorMessage());
            return null;
        }

        byte[] results = scanResults.getScanResults();
        logger.info(">>>>>>>>>获取扫描报告成功!|报告reportId:{}", reportId);
        return results;
    }

    /**
     * 获取所有扫描项目的扫描结果信息
     * @param portalClient
     * @return
     */
    public static List<ScanData> getScanDisplayData(PortalClient portalClient) {
        CxWSResponseScansDisplayData scansDisplayData =
                portalClient.portal.getScansDisplayDataForAllProjects(portalClient.sessionId);
        if (!scansDisplayData.isIsSuccesfull()) {
            logger.error(">>>>>>>>>获取所有扫描项目的扫描结果信息失败!|错误信息:{}", scansDisplayData.getErrorMessage());
            return null;
        }
        List<ScanDisplayData> displayDataList = scansDisplayData.getScanList().getScanDisplayData();
        if (null == displayDataList || 0 == displayDataList.size()) {
            logger.info(">>>>>>>>>获取所有扫描项目的扫描结果信息为空!");
            return null;
        }
        ArrayList<ScanData> scanDataArray = new ArrayList<>();
        ScanData scanData;
        for (int i = 0; i < displayDataList.size(); i++) {
            scanData = new ScanData();
            ScanDisplayData scanDisplayData = displayDataList.get(i);
            BeanUtil.copyPropertiesQuietly(scanDisplayData, scanData);
            scanData.setAllowedToDelete(scanDisplayData.isIsAllowedToDelete());
            scanData.setQueuedDateTime(PortalUtil.parseCxDateTime(scanDisplayData.getQueuedDateTime()));
            scanData.setFinishedDateTime(PortalUtil.parseCxDateTime(scanDisplayData.getFinishedDateTime()));
            scanData.setIncremental(scanDisplayData.isIsIncremental());
            scanData.setLoc(scanDisplayData.getLOC());
            scanData.setScanDisplayType(scanDisplayData.getScanDisplayType().value());
            scanData.setStatisticsCalculationDate(PortalUtil.parseCxDateTime(scanDisplayData.getStatisticsCalculationDate()));
            scanData.setPublic(scanDisplayData.isIsPublic());
            scanData.setLocked(scanDisplayData.isIsLocked());
            scanDataArray.add(scanData);
        }
        return scanDataArray;
    }

    /**
     * 取消当前扫描任务
     * @param portalClient
     * @param runId
     */
    public static void cancelScan(PortalClient portalClient, String runId) {
        CxWSBasicRepsonse basicRepsonse = portalClient.portal.cancelScan(portalClient.sessionId, runId);
        if (!basicRepsonse.isIsSuccesfull()) {
            logger.error(">>>>>>>>>取消当前扫描任务失败!|错误信息:{}", basicRepsonse.getErrorMessage());
            return;
        }
        logger.info(">>>>>>>>>取消当前扫描任务成功!|runId:{}", runId);
    }

    /**
     * 获取当前扫描Engine队列中任务数量
     * @param portalClient
     * @return
     */
    public static int getQueueCount(PortalClient portalClient) {
        CxWSResponseScanStatusArray scanStatusArray = portalClient.portal.getScansStatuses(portalClient.sessionId);
        if (!scanStatusArray.isIsSuccesfull()) {
            logger.error(">>>>>>>>>获取当前扫描Engine队列中任务数量失败!|错误信息:{}", scanStatusArray.getErrorMessage());
            return 0;
        }

        ArrayOfCxWSResponseScanStatus arrayScanStatus = scanStatusArray.getStatusArr();
        if (null == arrayScanStatus) {
            logger.info(">>>>>>>>>获取当前扫描Engine队列中ArrayOfCxWSResponseScanStatus为空!");
            return 0;
        }

        List<CxWSResponseScanStatus> listScanStatus = arrayScanStatus.getCxWSResponseScanStatus();
        if (null == listScanStatus) {
            logger.info(">>>>>>>>>获取当前扫描Engine队列中List<CxWSResponseScanStatus>为空!");
            return 0;
        }
        int queueCount = listScanStatus.size();
        logger.info(">>>>>>>>>>>>>>>当前扫描Engine队列中任务数量: {}", queueCount);
        return queueCount;

    }

    /**
     * 判断当前扫描结果是否已经结束
     * @param status
     * @return
     */
    public static boolean isScanEnd(TaskStatus status) {
        return status == TaskStatus.CANCELED || status == TaskStatus.DELETED || status == TaskStatus.FAILED
                || status == TaskStatus.FINISHED;
    }
}
